package lj.netty.websocket;

import cn.hutool.http.HttpUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import lj.netty.websocket.bo.WebSocketMsgBo;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;

import static io.netty.handler.codec.http.HttpUtil.isKeepAlive;

@Slf4j
@ChannelHandler.Sharable
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    /**
     * 建立连接
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        SocketUtils.addChannel(ctx.channel());
    }

    /**
     * 断开连接
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        SocketUtils.removeChannel(ctx.channel());
        WebSocketMsgBo webSocketMsgBo = new WebSocketMsgBo();
        webSocketMsgBo.setChannelId(ctx.channel().id());
        onClose(webSocketMsgBo);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (null == msg) {
            return;
        }
        if (msg instanceof CloseWebSocketFrame) {
            ctx.close();
            return;
        }
        if (msg instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(((WebSocketFrame) msg).content().retain()));
            return;
        }
        WebSocketMsgBo webSocketMsgBo = new WebSocketMsgBo();
        webSocketMsgBo.setChannelId(ctx.channel().id());
        if (msg instanceof FullHttpRequest) {
            FullHttpRequest request = (FullHttpRequest) msg;
            //若不是websocket方式，则创建BAD_REQUEST的req，返回给客户端
            if (!request.decoderResult().isSuccess() || (!"websocket".equals(request.headers().get("Upgrade")))) {
                sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
                return;
            }
            String uri = request.uri();
            //如果url包含参数，需要处理(目的是交由下级 WebSocketServerProtocolHandler处理，三次握手)
            if (uri.contains("?")) {
                String newUri = uri.substring(0, uri.indexOf("?"));
                request.setUri(newUri);
            }

            Map<String, String> paramMap = HttpUtil.decodeParamMap(uri, CharsetUtil.UTF_8);

            webSocketMsgBo.setParamMap(paramMap);
            this.initChannel(webSocketMsgBo);

            //  连接时前台传入的是token, 务必校验token，防止恶意连接
//            Object target = JWTUtil.parseToken(token).getPayload("userId");
//            if (target == null || !verify) {
//                sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
//                return;
//            }
//            //绑定uuid到channel 使用忽略平台登录方式的 token 作为目标绑定
//            SocketUtils.bindChannel(getTarget(target), ctx.channel().id());
        } else if (msg instanceof TextWebSocketFrame) {
            //正常的TEXT消息类型
            TextWebSocketFrame frame = (TextWebSocketFrame) msg;
            String result;
            if ("PING".equals(frame.text())) { // 心跳消息
                result = "PONG";
            } else {
                webSocketMsgBo.setMsg(frame.text());
                result = this.onMsg(webSocketMsgBo);
            }
            ctx.channel().writeAndFlush(new TextWebSocketFrame(result));
        }
        super.channelRead(ctx, msg);
    }

    /**
     * 消息到达触发
     * @param webSocketMsgBo
     * @return
     */
    public String onMsg(WebSocketMsgBo webSocketMsgBo) {
        return webSocketMsgBo.getMsg();
    }

    /**
     * 连接断开触发
     * @param webSocketMsgBo
     */
    public void onClose(WebSocketMsgBo webSocketMsgBo) {}

    /**
     * 连接创建触发
     * @param webSocketMsgBo
     */
    public void initChannel(WebSocketMsgBo webSocketMsgBo) {}


    /**
     * 反馈非法请求
     *
     * @param ctx
     * @param req
     * @param res
     */
    private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) {
        // 返回应答给客户端
        if (res.status().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
        }
        ChannelFuture f = ctx.channel().writeAndFlush(res);
        // 如果是非Keep-Alive，关闭连接
        if (!isKeepAlive(req) || res.status().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (IdleState.READER_IDLE.equals(event.state())) {
                SocketUtils.removeChannel(ctx.channel());
            } else if (IdleState.WRITER_IDLE.equals(event.state())) {

            } else if (IdleState.ALL_IDLE.equals(event.state())) {
                //未进行读写操作
            }
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {

    }

    private String getTarget(String target) {
        String[] split = target.split("-");
        if (split.length > 3) {
            return target.substring(2);
        }
        return target.substring(target.indexOf("-")+1);
    }

}
